package com.myjpa.common.log;

import com.myjpa.common.json.JacksonMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: yaoll
 * @date: 2020-09-04
 * @verison: 1.0
 */
@Slf4j
public class LogUtils {


	private static final AtomicInteger flag = new AtomicInteger(10000000);

	private static final ThreadLocal<Data> dataStorage = new ThreadLocal<>();

	public static void doBefore(JoinPoint joinPoint) {
		// 接收到请求，记录请求内容
		Data data = initFlag();

		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		String name = request.getRequestURI();
		data.methodName = name;
		// 记录下请求内容
		log.info("{} {} REQUEST URL      : {} {} {}", data.intFlag, name, request.getMethod(), request.getRequestURL().toString(), request.getQueryString());
		log.info("{} {} REQUEST Handler  : {}.{}", data.intFlag, name, joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
		log.info("{} {} REQUEST ARGS     : {}", data.intFlag, name, JacksonMapper.toSimpleJson(prepare(joinPoint)));
	}

	public static void doAfterReturning(Object ret) {
		// 处理完请求，返回内容
		String response = JacksonMapper.toSimpleJson(ret);
		Data data = dataStorage.get();
		String name = data.methodName;
		log.info("{} {} REQUEST RESPONSE : {}", data.intFlag, name, response.length() > 400 ? response.substring(0, 400) : response);
		log.info("{} {} REQUEST DURATION : {}", data.intFlag, name, System.currentTimeMillis() - data.timestamp);
		dataStorage.remove();
	}

	public static void doAfterThrowing(Throwable ex) {
		Data data = dataStorage.get();
		String name = data.methodName;
		log.error("{} {} REQUEST EXCEPTION: {}", data.intFlag, name, ex.getMessage());
		log.error("{} {} REQUEST EXCEPTION:", data.intFlag, name, ex);
		log.error("{} {} DURATION : {}", data.intFlag, name, System.currentTimeMillis() - data.timestamp);
		dataStorage.remove();
	}

	public static List<Object> prepare(JoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		List<Object> list = new LinkedList<>();
		if (args != null && args.length > 0) {
			for (Object arg : args) {
				if (arg instanceof ServletRequest) {
					continue;
				}
				if (arg instanceof ServletResponse) {
					continue;
				}
				if (arg instanceof MultipartFile) {
					continue;
				}
				list.add(arg);
			}
		}
		return list;
	}

	private static Data initFlag() {
		Data data = new Data();
		data.timestamp = System.currentTimeMillis();

		int result = flag.addAndGet(1);
		if (result >= 99999999) {
			flag.set(10000000);
		}
		data.intFlag = result;
		dataStorage.set(data);
		return data;
	}

	private static class Data {
		Integer intFlag;
		Long timestamp;
		String methodName;
	}
}
